#pragma once
#include "Types.h"

#pragma warning(disable: 4996)

int yylex(void);

#define INTERMEDIATE_CODE_DATA_SIZE 0x10000
#define IDENTIFIER_NAME_MAX_LEN 0x100
#define JMP_LABEL_DATA_SIZE 0x100

// If 1, is register. Else is memory.
typedef BOOL REG_OR_MEM;

// Memory location
typedef UInt32 Address;

enum OPERATION_TYPE
{
	OT_MOV_CONSTANT_TO_REGISTER,
	OT_ADD_CONSTANT_TO_REGISTER,
	OT_SUB_CONSTANT_FROM_REGISTER,
	OT_INC_REGISTER,
	OT_JMP_CONSTANT,
	OT_CMP_REGISTER_AND_CONSTANT,
	OT_JMP_CONSTANT_IF_CONDITION,
	OT_JMP_LABEL,
	OT_RET
};

enum REGISTER
{
	// 8 General Purpose 32 Bit Registers
	R_EAX, R_ECX, R_EDX, R_EBX, R_ESP, R_EBP, R_ESI, R_EDI,
	
	// 8 General Purpose 16 Bit Registers
	R_AX, R_CX, R_DX, R_BX, R_SP, R_BP, R_SI, R_DI,
	
	// 8 General Purpose 8 Bit Registers
	R_AL, R_CL, R_DL, R_BL, R_AH, R_CH, R_DH, R_BH
};

enum JMP_CONDITION
{
	JC_JA,
	JC_JAE,
	JC_JB,
	JC_JBE,
	JC_JE,
	JC_JG,
	JC_JGE,
	JC_JL,
	JC_JLE,
	JC_JNA,
	JC_JNAE,
	JC_JNB,
	JC_JNBE,
	JC_JNE,
	JC_JNG,
	JC_JNGE,
	JC_JNL,
	JC_JNLE,
	JC_JNZ
};

//extern static const struct RegisterInfo registerInfos[];

enum IDENTIFIER_TYPE
{
	IDT_JMP_LABEL,
	IDT_CONSTANT
};

struct Identifier
{
	Int8 ascii[IDENTIFIER_NAME_MAX_LEN];
	enum IDENTIFIER_TYPE type;
};

///////////////////////////////////////////////////////////
// Subclasses of Identifier
struct JmpLabelIdentifier
{
	UInt instructionCount;
};

struct MemoryOrRegister
{
	REG_OR_MEM type;

	union
	{
		enum REGISTER reg;
		Address mem;
	};
};

struct RegisterInfo
{
	// Size in bytes
	UInt size;

	// Code of the register for code generation
	UInt code;
};

struct Instruction
{
	enum OPERATION_TYPE type;
};


///////////////////////////////////////////////////////////
// Sub classes of Instruction

struct ConstantToRegister
{
	Int src;
	enum REGISTER dest;
};

#define MovConstantToRegister ConstantToRegister
#define AddConstantToRegister ConstantToRegister
#define SubConstantFromRegister ConstantToRegister

struct RegisterAndConstant
{
	enum REGISTER reg;
	Int constant;
};

#define CmpRegisterAndConstant RegisterAndConstant

struct JmpLabel
{
	struct JmpLabelIdentifier* label;
};

struct JmpConstantIfCondition
{
	enum JMP_CONDITION condition;
	Int constant;
};

struct IncRegister
{
	enum REGISTER reg;
};

struct JmpConstant
{
	Int constant;
};

#define ADD_BASIC_INSTRUCTION(INSTRUCTION_TYPE)\
	intermediateCode->type = INSTRUCTION_TYPE;\
	++intermediateCode;

#define ADD_INSTRUCTION(INSTRUCTION_STRUCT, INSTRUCTION_TYPE)\
	((INSTRUCTION_STRUCT*)(intermediateCode + 1));\
	intermediateCode->type = INSTRUCTION_TYPE;\
	intermediateCode = (struct Instruction*)(((char*)intermediateCode) + (sizeof(INSTRUCTION_STRUCT) + sizeof(struct Instruction)));

#define ADD_IDENTIFIER(ASCII, IDENTIFIER_STRUCT, IDENTIFIER_TYPE)\
	(identifiers + 1);\
	identifiers->type = IDENTIFIER_TYPE;\
	strcpy(identifiers->ascii, ASCII);\
	(UInt8*)identifiers += (sizeof(struct Identifier) + sizeof(IDENTIFIER_STRUCT));

extern struct Instruction* intermediateCode;
extern struct Identifier* identifiers;